#!/bin/bash

        #-----------------------------------------------------------#
        #   This program is used to make emerge output pretty       #
        #   Version: 20141220                                       #
        #                                           -ppurka         #
        #-----------------------------------------------------------#

        #--------------------- License: GPL-3  ---------------------#
        # Copyright (C) 2009 - 2014  ppurka _at_ gmail _dot_ com    #
        # This program comes with ABSOLUTELY NO WARRANTY;           #
        # This is free software, and you are welcome to redistribute#
        # it under certain conditions;                              #
        #                                                           #
        # For a brief summary of the license see the help text of   #
        # this program by executing:                                #
        # quietemerge --help                                        #
        #                                                           #
        # For the full text of the license see                      #
        # http://www.gnu.org/licenses/gpl-3.0.html                  #
        #-----------------------------------------------------------#

        #---------------------  Changelog  -------------------------#
        #                                                           #
        # 12/20/2014:   Recent change in emerge output broke the    #
        #               whole script! This is fixed.                #
        # 05/07/2014:   In errors the pkg name is now $P::reponame. #
        # 02/05/2014:   Change the color of time taken to normal.   #
        # 04/27/2013:   Bugfix: Location of make.conf has changed.  #
        #               Bugfix: NOCOLOR was working in reverse.     #
        # 04/13/2012:   Update to new message @ autounmask-write    #
        # 04/06/2012:   Bugfix: Often term titlebar showed that more#
        #               jobs were running than the actual number.   #
        # 01/22/2012:   Bugfix: Sometimes skipped pkgs weren't being#
        #               detected. This was quite hard to reproduce. #
        # 01/15/2012:   Bugfix: Reintroduce -q. Don't remove it ever#
        # 12/30/2011:   Bugfix: Avoid infinite recursion.           #
        # 12/24/2011:   Give pkg name in term title if # jobs is 1. #
        #               Execute emerge directly on unsupported args.#
        # 12/18/2011:   Bugfix: Force --color=n on actual emerge.   #
        #               Clean up help() function.                   #
        # 12/05/2011:   Bugfix: Get rid of "grep: Unmatched [ or [^"#
        # 11/27/2011:   Provide total ETA without 'binary' emerges. #
        # 11/26/2011:   Provide info before running pre/post command#
        # 11/13/2011:   --quiet-build is the default. Remove -q.    #
        # 08/27/2011:   Now, easier to detect currently merging pkgs#
        #               Bugfix: Correct <num>/<total> in term title.#
        # 08/20/2011:   Bugfix: Detect "binary" @ emerge -K output. #
        # 06/17/2011:   Bugfix: autounmask-write@EMERGE_DEFAULT_OPTS#
        #               wasn't working properly.                    #
        #               Update the config update grep string to inc-#
        #               lude config message from --autounmask-write #
        # 06/11/2011:   export FEATURES="notitles"-no need to update#
        #               xterm title so frequently now.              #
        #               Support for --autounmask-write.             #
        # 05/21/2011:   Use wc -[wl] with <$file to avoid extra o/p.#
        #               Secure TMPFILE should run after mount_tmpfs.#
        #               Output the total time emerge took.          #
        #               Bug fix: Revert sed expr for my_genlop -t $p#
        # 05/20/2011:   Bug fix: grep -n -> grep -nF @ my_genlop_eta#
        #               Bug fix: Move/^[ebuild/!d back to after grep#
        #               Bug fix: Fix sed expr in my_genlop. ETA was #
        #               incorrect if pkgs like rox and rox-lib were #
        #               both installed. rox would've the wrong ETA. #
        # 05/18/2011:   Major rewrite with --jobs support (finally!)#
        # 05/14/2011:   Restart --jobs support, one func at a time! #
        # 05/04/2011:   Don't create $tmpT if $status -ne 0.        #
        # 05/01/2011:   Bug fix: "_ETA_ALL=1 $self" wasn't working, #
        #               i.e. ETA_ALL in config wasn't over-ridden.  #
        # 04/30/2011:   Recognize --color=n given in command line.  #
        #               Bug fix: work around duplicates in E_LOG.   #
        #               Bug fix: Increase max length of ETA to 15.  #
        #                   This _may_ fix "any time no" in output  #
        # 04/27/2011:   Create config file only if run as root.     #
        #               Add option to show per-package ETA.         #
        #               Add rudimentary detection of old config file#
        #               Bug fix: Pass --resume on to emerge command.#
        # 04/23/2011:   Make sure tmpT is secured only just prior to#
        #               writing to it                               #
        # 04/22/2011:   Replace genlop with my own function. This is#
        #               less featureful but faster and less buggy   #
        #               than genlop. This is also needed for future #
        #               --jobs support.                             #
        #               Introduce e_opt_resume to handle both -r and#
        #               --resume command line arguments.            #
        # 12/31/2010:   Bug fix: Increase total_len by 3. mplayer   #
        #               and thinkfinger were making it exceed 80char#
        # 10/27/2010:   Bug fix: Handle "Failed to install" errors  #
        # 10/24/2010:   Add err pattern when ebuild file disappears #
        #               during  emerge.                             #
        # 05/25/2010:   Add 2>/dev/null to which (thx to JustJoe at #
        #               gentoo forums)                              #
        #               Start of support for --jobs                 #
        # 04/07/2010:   Add config option for a pre- and post- cmd. #
        # 02/22/2010:   Update copyright year.                      #
        # 01/20/2010:   Avoid grep -A3. sed can do the job just fine#
        # 01/19/2010:   Partly revert genlop -f $tmpT so that genlop#
        #               gives the correct output with multiple $self#
        #               Now genlop -c doesn't use tmpT (still buggy)#
        #               Reimplement multiple instances              #
        # 01/17/2010:   Revert all multiple instances changes X-(   #
        # 01/16/2010:   Bug fix: revert check for mult. instances   #
        #               while mounting tmpfs                        #
        #               Bug fix: fix pgrep command and count of self#
        #               Bug fix: Arrrgghhh! work around genlop bugs #
        # 01/09/2010:   Initial support for multiple instances of   #
        #               quietemerge                                 #
        # 12/21/2009:   Change output during next iteration if term #
        #               size changes.                               #
        # 12/19/2009:   Bug fix: Catching ACCESS VIOLATION should be#
        #               less buggy now.                             #
        # 12/05/2009:   Genlop should be slightly faster now.       #
        # 12/02/2009:   Enable support for environment variables,   #
        #               which can temporarily override the settings #
        #               in the config file.                         #
        #               Update help text.                           #
        # 11/21/2009:   Bug fix: Really arcane bug! Packages with   #
        #               fetch restrictions error out so fast that   #
        #               the packages' status is not even displayed! #
        #               So, I had to change sleep $DELAY to sleep 1 #
        #               in the first while loop.                    #
        # 11/20/2009:   Bug fix: Check for old temp files and delete#
        #               them if needed. This fixes security concern #
        #               raised by 0x4a47 at gentoo forums.          #
        #               Bug fix: Also, rename tmp files a little bit#
        #               Bug fix: Check for fetch restricted packages#
        #               Respect --ask set in EMERGE_DEFAULT_OPTS    #
        # 11/12/2009:   Show # of skipped and # packages left.      #
        #               Show preserved libs found and do conf update#
        #               even if some package has failed.            #
        # 11/11/2009:   Bug fix: ACCESS VIOLATION SUMMARY wasn't    #
        #               being caught. This might still be buggy :-/ #
        #               Bug fix: Some packages would output many    #
        #               lines on the countdown.                     #
        #               Bug fix: --resume didn't use to update term #
        #               title. Removed check for --resume now. This #
        #               also allows us to now always remove $tmpE.  #
        #               Show the # of failed packages on exit.      #
        #               Thanks to jw5801 and jprobichaud on gentoo  #
        #               forums for discussion and ideas regarding   #
        #               usage of genlop. Using genlop -f now for the#
        #               merge time calculation.                     #
        #               Broke off all common portions of the script #
        #               into their own functions.                   #
        #               All grep should have LC_ALL="C" in front.   #
        # 11/05/2009:   Bug fix: Workaround bug in genlop again.    #
        #               Bug fix: Pressing just enter should take in #
        #               "yes" as the answer when --ask is used.     #
        # 11/02/2009:   Determine the PORTAGE_TMPDIR by portageq,and#
        #               mount tmpfs on to PORTAGE_TMPDIR/portage    #
        #               Added a new config option: SHOW_MERGE_TIME  #
        #               if it is 1, then time required to merge will#
        #               be shown instead of "Done!"                 #
        #               Bug fix: let us be less exacting on genlop. #
        # 10/30/2009:   Bug fix: Remove the skipped package from tmp#
        #               file so that the correct count and correct  #
        #               remaining time is given by genlop           #
        # 10/29/2009:   Genuine support for --keep-going.           #
        #               Actually calculate the space required by    #
        #               genlop output.                              #
        #               "Time Left" -> "ETA" -- this is shorter     #
        #               Simpler countdown if terminal width is small#
        #               $Self owns the term title, rewrite always!  #
        #               Bug fix: Hopefully, emerge won't output to  #
        #               terminal anymore. Use >& instead of 2>&1    #
        #               Bug fix: Don't output a bogus "Total ETA" if#
        #               emerge failed during --pretend              #
        # 10/27/2009:   Bug fix: Separate $tmpE for $USER and root  #
        #               Bug fix: tmpE should be rm'd on success     #
        # 10/26/2009:   Bug fix: Include infon from my_bash_function#
        # 10/25/2009:   Change grep to LC_ALL=C grep.               #
        #               Give total ETA even with --pretend.         #
        #               Bug fix: overlay was also being printed.    #
        # 10/23/2009:   Exit if --jobs is given.                    #
        #               Give total emerge time est. if -a is present#
        #               Check for root only after pretend stage, so #
        #               even non-root users can use this with -p    #
        #               Add -q to genlop command.                   #
        #               Check MOUNT_TMPFS before unmounting         #
        #               Bug fix: Check whether PID is empty before  #
        #               trying to kill emerge                       #
        # 10/17/2009:   Bug fix: I guess no one tried --resume with #
        #               this script. It was definitely broken ;-)   #
        #               Initial code for --ask. The code can now    #
        #               potentially handle other emerge switches too#
        # 10/12/2009:   Bug fix: avoid setting terminal title when  #
        #               we are in console.                          #
        #               Bug fix: sometimes wrong package count would#
        #               be shown in terminal titlebar.              #
        #               Bug fix: if no pkg has actually been emerged#
        #               then $len remains empty, and a bash error   #
        #               occurs.                                     #
        #               Avoid running genlop when package is being  #
        #               installed (i.e. in install/qmerge stage)    #
        #               Add a Version and a vim Modeline :)         #
        # 10/11/2009:   For eventual release into the gentoo forum: #
        #               1. lots of error checks                     #
        #               2. create a config file. The defaults are   #
        #                  sane. It should not matter if the user   #
        #                  does not modify the config file.         #
        #               3. make tmpfs mounting optional,- disabled  #
        #                  by default.                              #
        #               4. Add a license.                           #
        #               5. Add the functions from my_bash_functions #
        #                  inline in this script.                   #
        # 10/09/2009:   Updated script to show the correct count of #
        #               packages. Also, this warns on B Blocks and  #
        #               interactive packages.                       #
        # 10/03/2009:   First version of the script                 #
        #-----------------------------------------------------------#

# Set the colors that will be used  # {{{
cl="$(tput el)"         # clear till end of line
blue="\x1b[1;34m"
bold="\x1b[1m"
cyan="\x1b[1;36m"
green="\x1b[1;32m"
normal="\x1b[0m"
pink="\x1b[1;35m"
red="\x1b[1;31m"
reverse="\x1b[7m"
underline="\x1b[4m"
yellow="\x1b[1;33m"

COLORS=( blue bold cyan green normal pink red reverse underline yellow )

# Print out information.
# Usage: info "Whatever you want to print"
info(){
    echo -e "  $yellow*$normal ${@}"
}

# Print out information without ending with a newline. This adds a space at end
# Usage: infon "Whatever you want to print"
infon(){
    echo -ne "  $yellow*$normal ${@} "
}

# info with carriage return at end
infor() {
    echo -ne "  $yellow*$normal $@$cl\r"
}

# Print out error information.
# Usage: Err "For printing errors"
# Usage: Err -w "For printing warnings"
Err(){
    local color="$red" msg="ERROR"
    [[ "$1" = "-w" ]] && color="$pink" && msg="WARNING" && shift
    echo -e "  $color*$normal [$color $msg!!$normal ] ${@}" >&2
}

die() {
    Err "$@"
    exit 1
}

# Usage: centered_output dash|blank "text string"
# where text is less than cols characters in length
centered_output() {
    local cols=$(tput cols)
    local len="$(echo "$2" | sed -e 's#\\x1[bB]\[[^m]\+m##g')"
    len=${#len}
    if [[ $len -ge $cols ]]; then
        echo -e "$2"
        return
    fi

    # Create D_ASH and BLANK dynamically according to terminal width
    # Solution got via google:
    # http://www.unix.com/shell-programming-scripting/46584-repeat-character-printf.html#post302150959
    printf -vBLANK "%${cols}s" ""
    local D_ASH="${BLANK// /-}"
    local begin=$(( $cols/2 - $len/2 ));    # Length of dash|blank before text
    local end=$(( $cols - $begin - $len )); # Length of dash|blank after text
    if [[ -z "$2" ]]; then
        if [[ "$1" = "dash" ]]; then echo $D_ASH; return
        else echo; return
        fi
    fi
    if [[ "$1" = "dash" ]]; then
        echo -e "${D_ASH:1:$begin}$reverse${2//\\x1[bB]\[0m/\x1b[0m$reverse}\
$normal${D_ASH:1:$end}"
    else
        echo -e "${BLANK:1:$begin}$reverse${2//\\x1[bB]\[0m/\x1b[0m$reverse}\
$normal${BLANK:1:$end}"
    fi
}
# }}}

setup_config_file() {
    info "$yellow$config_dir/$config_file$normal config file not found. Creating ...
    "
    [[ ! -d "$config_dir" ]] && {
        mkdir -p "$config_dir" || die "Could not create $config_dir"
    }
    cat <<END > "$config_dir/$config_file"
# quietemerge config file
#
# Delay after which genlop will be called (in a loop).
# Default is 5 seconds.
# DELAY=5
#
# Should we mount tmpfs on to /var/tmp/portage? This should be used only
# if you have enough RAM. For example emerging gcc with +java USE flag
# requires about 2G RAM for storage. Emerging openoffice probably requires
# even more. Change value to 1 to enable tmpfs. Default is 0 (no).
# MOUNT_TMPFS=0
#
# Amount of RAM that will be mounted on to /var/tmp/portage if MOUNT_TMPFS
# is 1.
# See the man page of mount for the values that can be given to TMPFS_SIZE.
# Look for the valid values of the "size" mount option of tmpfs.
# Default is half the size of your total RAM.
# TMPFS_SIZE=50%
#
# The console based program that is used to update /etc,/usr config files
# Examples of such programs are cfg-update, dispatch-conf, etc-update,
# etc-proposals, conf-update
# Default is empty.
# TXT_UPDATER=""
#
# The graphical program that is used to update /etc,/usr config files
# Examples are cfg-update, etc-proposals.
# Default is TXT_UPDATER
# GUI_UPDATER="\$TXT_UPDATER"
# 
# Whether we should display the latest merge time instead of "Done!" after
# a package has been emerged. Default is 0 (no). Set it to 1 to enable it.
# SHOW_MERGE_TIME=0
#
# You can set a pre-command here which will be run just before the actual
# emerge is started. If the MOUNT_TMPFS option is enabled, then it is run
# just after the tmpfs is mounted.
# Default is empty.
# PRE_CMD=""
#
# You can also set a post-command which is run just before the tmpfs is
# unmounted and after any cleanups performed by the script itself is done.
# This post-command will be run even if the script is killed by pressing
# for example Ctrl-C.
# Default is empty.
# POST_CMD=""
#
# If you want to show the estimated emerge time for all the individual
# packages, before the actual emerge (i.e. during the "Pretended emerge"
# stage), then set ETA_ALL to 1.
# Default is 0
# ETA_ALL=0

END

info "Open $yellow$config_dir/$config_file$normal and modify the settings
    to your own liking.
    "
    sleep 2
}

# Help me!! {{{
help() {
    echo -e "\n $yellow $self:$normal"
    info "This program is used to perform the emerge with pretty output.
    Version: $( sed -n -e '5s/^.*:[ ]\+\([0-9]\+\) .*$/\1/p' "$0" )
    "
    info "License: GPL-3
    Copyright (C) 2009 - 2014  P. Purkayastha (ppurka _at_ gmail _dot_ com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    "

    info "Usage:     $self [<options>]"
    info "Options:
    -h | --help     Show this help text

    All other options are passed directly to emerge.
    If no arguments are provided to $self, then this help text is shown.
    "
    info "If any arguments that are unsupported by this script is detected
    then the script will execute emerge directly, bypassing all the setup
    in the rest of the script.
    A few examples of such unsupported arguments are:
    ${red}--changelog, --depclean, --search, --sync, --unmerge$normal, etc.
    See the function ${bold}has_unsupported_arg$normal in line $( sed -n \
    '/^has_unsupported_arg()/=' "$0" ) of $self
    for examples of more unsupported arguments.
    "
    info "Examples on how to call this script:
    $self -1 glibc vim
    $self --resume
    $self -auDvN --jobs=3 --load-average=4 --keep-going world
    "
    # No configuration file is created for non-root. So, don't show this.
    [[ $UID -eq 0 ]] && {
        info "Configuration:
    Some basic configuration related to this script is stored in
    $yellow$config_dir/$config_file$normal
        "
        info "In order to temporarily override the settings present in the
    config file, prepend the variable with an underscore _ and provide it
    as an environment variable.
    An example where I want to override the config file setting
    temporarily, and/or provide a temporary setting:
    _DELAY=2 _MOUNT_TMPFS=1 $self -a eselect
        "
    } || \
        info "You can set the environment variable ${bold}_ETA_ALL=1$normal
    to get the estimated time of emerge for each individual package.

    Note: This script creates a config file in the home directory (of root)
    only when run as root.
        "
    exit 0
}
# }}}

self="${0##*/}"
config_dir=$HOME/.config
config_file="${0##*\/}.config"
# Display help in xterm if user clicked on the file
if [[ -z "$@" && "$DISPLAY" ]] && ! tty -s; then
    exec xterm -hold -e "$(dirname "$0")/$(basename "$0")" -h
elif [[ -z "$@" || "$@" = -h* || "$@" = --h* ]]; then
    help
fi

# Source the config file. Set it up only if called by root
[[ $UID -eq 0 ]] && \
    if [[ -f "$config_dir/$config_file" ]]; then
        . "$config_dir/$config_file"
        # Search for the latest config variable in the config file. If this
        # variable is not present, then it (hopefully) means that the
        # config file is not the latest one
        # Currently latest config variable is ETA_ALL
        if ! grep -qs ETA_ALL "$config_dir/$config_file"; then
            Err -w "Your config file $yellow$config_dir/$config_file$normal
    needs updating.
    To update your config file, rename the old file and rerun this script.
    Then use your favorite diff program to merge the old and the new file.
    "
        fi
    else
        setup_config_file
        exit
    fi


# Variables used in this script. Modify only if you know what you are doing
# temp files
TMPFILE=/tmp/$self.output.log
tmpE=/tmp/$self.tmpE.$UID
tmpT=/tmp/$self.tmpT
# Variables from config files or environment. environment gets preference
: ${_DELAY:=${DELAY:-5}}
: ${_MOUNT_TMPFS:=${MOUNT_TMPFS:-0}}
: ${_TMPFS_SIZE:=$TMPFS_SIZE}
: ${_TXT_UPDATER:=$TXT_UPDATER}
: ${_GUI_UPDATER:=$GUI_UPDATER}
: ${_SHOW_MERGE_TIME:=${SHOW_MERGE_TIME:-0}}
: ${_PRE_CMD:=$PRE_CMD}
: ${_POST_CMD:=$POST_CMD}
: ${_ETA_ALL:=${ETA_ALL:-0}}
unset DELAY MOUNT_TMPFS TMPFS_SIZE TXT_UPDATER GUI_UPDATER \
    SHOW_MERGE_TIME PRE_CMD POST_CMD
# Other variables used in the script.
declare -a -i exit_status=( 0 0 ) # Count # of failed and skipped packages
declare -a -i merge_eta     # Keep in memory the est. ETA of current pkg(s)
declare -a -i merge_start   # Keep in memory start time of current emerge(s)
declare -i count_lines=1    # Count the number of lines read from TMPFILE
declare -i e_opt_ask=0      # If it is 1, then ask before emerging
declare -i e_opt_pretend=0  # If it is 1, then only pretend the emerge
declare -i my_len=19        # This is the length of "  * Emerging . ETA:"
declare -i num_line_elog    # Store the total # of lines in old emerge.log
declare -i num_self=1       # The number of instances of $self running
declare -i _total_num       # Holds the total # of pkgs going to be merged
declare -i PID              # The pid of emerge process
cmdline=""                  # This will hold the arguments passed to emerge
E_LOG="/var/log/emerge.log" # The file where emerge dumps its logs
e_opt_auto=""               # Variable for --autounmask-write
e_opt_color="--color=y"     # This will be set to --color=n if so asked for
e_opt_resume=""             # If it is '--resume', display "Resuming merge"
e_opt_verbose=""            # If it is -v, then emerge -vq is launched
eta=""                      # Total time left for the remaining packages
merge_eta[0]=0              # This will hold the total eta for all pkgs
newlines=""                 # New lines added to TMPFILE since prev while()
pkg_all=""                  # Holds the list of all packages
pkg_installing=""           # Hold the list of package currently installing
pkg_merging=""              # Hold the list of package currently merging


# Mounting and unmounting tmpfs. These fns take no argument #{{{
mount_tmpfs() {
    [[ $_MOUNT_TMPFS -ne 1 ]] && return 0
    local var_tmp="$(portageq envvar PORTAGE_TMPDIR)/portage"
    if mount | grep -q -s -w "tmpfs on $var_tmp"; then
        info "tmpfs is already mounted\n"
    elif [[ "$_TMPFS_SIZE" ]]; then
        mount -t tmpfs -o size=${_TMPFS_SIZE} tmpfs $var_tmp
    else
        mount -t tmpfs tmpfs $var_tmp
    fi
    [[ $? -ne 0 ]] && die "Error in mounting tmpfs\n"
}
unmount_tmpfs() {
    # Do not mess with tmpfs mounted without this script
    [[ $_MOUNT_TMPFS -ne 1 ]] && return 0
    local var_tmp="$(portageq envvar PORTAGE_TMPDIR)/portage"

    # Check if $var_tmp has any "leftover" files.
    if [[ "$( ls $var_tmp/* 2>/dev/null )" ]]; then
        Err -w "It appears that $blue$var_tmp$normal is unclean.
    This may be because a different instance of $self has exited with
    an error. 
    tmpfs on $blue$var_tmp$normal will not be unmounted."
    elif [[ $UID -eq 0 ]] && mount | grep -qsw "tmpfs on $var_tmp"; then
        umount $var_tmp
    fi
    [[ $? -ne 0 ]] && die "Error in unmounting tmpfs\n"
} 
# }}}

# Functions specific to this script {{{
# All the functions are present in alphabetical order.
 
# This is the function that was earlier in my_genlop_eta. Since I run
# my_genlop_eta in subshells, merge_eta wasn't being set.
# Usage: determine_merge_eta
determine_merge_eta() {
    # This else loop runs only during the "Pretended emerge" stage
    local -i cnt=1
    local p ptime
    while read p; do
        [[ "$p" =~ ^\[ebuild.* ]] || [[ "$p" =~ ^\[binary.* ]] || continue
        p="${p#*]}"
        # Change cat/pkg-ver to cat/pkg
        ptime="$( echo "$p" | \
            sed -e 's/^[ ]\+\([^ ]\+\)-[0-9]\{1,\}.*$/\1/' )"
        ptime="$( my_genlop $ptime )" || ptime=0
        merge_eta[${cnt}]=$ptime
        merge_eta[0]=$(( ${merge_eta[0]} + $ptime ))
        if [[ $_ETA_ALL -eq 1 ]]; then
            p="$( echo "$p" | sed -e 's/^[ ]\+\([^ ]\+\).*$/\1/' )"
            infolr "echo -e" "    $bold$p$normal: " "$( get_time $ptime )"
        fi
        ((cnt++))
    done <$tmpE
}

# Usage: determine_merge_eta_no_binary
determine_merge_eta_no_binary() {
    local -i eta_no_bin=${merge_eta[0]} cnt=1
    while read p; do
        [[ "$p" =~ ^\[ebuild.* ]] || [[ "$p" =~ ^\[binary.* ]] || continue
        [[ "$p" =~ ^\[binary.* ]] && (( eta_no_bin -= merge_eta[$cnt] ))
        ((cnt++))
    done <$tmpE
    echo $eta_no_bin
}

# Usage: exit_prog [<exit status>]
exit_prog() {
    [[ -f "$tmpE" ]] && rm -f $tmpE
    [[ -f "$tmpT" ]] && rm -f $tmpT
    echo        # I like echoes!
    # If emerge process is running and the script has reached here, then
    # kill the emerge process.
    if [[ "$PID" ]] && ps -p $PID >& /dev/null; then
        Err -w "The emerge process given by PID: $PID is still running.
    The emerge process will be killed.\n"
        kill $PID
        [[ $_MOUNT_TMPFS -eq 1 ]] && sleep 2     # Allow emerge to finish
    fi

    # Post command after everything is over.
    [[ "$_POST_CMD" ]] && info "Executing post-command: $_POST_CMD"
    [[ "$_POST_CMD" && -x "$( which "$_POST_CMD" 2> /dev/null )" ]] \
        && "$_POST_CMD" || eval "$_POST_CMD"

    # Unmount only if status=0 and only one $self process is running
    get_num_self
    [[ "$@" -eq 0 && $num_self -eq 1 ]] && unmount_tmpfs
    echo    # This is here so that the exit below takes status of $@
    exit $@
}

# check EMERGE_DEFAULT_OPTS for --ask. This function checks for -a or --ask
# and removes it if it is present.
# Usage: fix_edo 
fix_edo() {
    local edo="$( portageq envvar EMERGE_DEFAULT_OPTS )"
    local new_edo
    [[ "$edo" ]] || return
    set -- ${edo}
    until [[ -z "$1" ]]; do
        case "$1" in
            -a|--ask) e_opt_ask=1;;
            --autounmask-write*)
                    if [[ "$2" = "y" || "$2" = "n" ]]; then
                        e_opt_auto="--autounmask-write $2"
                        shift
                    else
                        e_opt_auto="$1"
                    fi ;;
            --color*)
                    if [[ "$1" = *=n ]]; then
                        e_opt_color="--color=n"
                    elif [[ "${edo/--color n}" != "$edo" ]]; then
                        e_opt_color="--color=n"
                    fi 
                    # Don't change the edo itself. Let it remain.
                    new_edo="$new_edo $1";;
            --*)    new_edo="$new_edo $1";;
            -*)     if [[ "$1" =~ -[b-zA-Z0-9]*a[b-zA-Z0-9]* ]]; then
                        new_edo="$new_edo ${1/a/}"
                        e_opt_ask=1
                    else
                        new_edo="$new_edo $1"
                    fi;;
            *)      new_edo="$new_edo $1";;
        esac
        shift
    done
    export EMERGE_DEFAULT_OPTS="$new_edo"
}

# get the number of self processes running.
# Usage: get_num_self
get_num_self() {
    # There is no easy way to get the number of running processes of $self
    # ps, pgrep, etc all reports multiple processes instead of the actual
    # number. I believe they count all the subprocesses too, if they are
    # called inside a $( ) construct.
    local t=$(mktemp /tmp/$self-pgrep-XXXX)
    pgrep -f "^[^ ]*($self|bash.*$self)" > $t 2>/dev/null
    num_self=$(wc -l <$t)
    rm -f $t
}

# Output the time in hours, min, etc, given the number of seconds
# Usage: get_time <# of seconds>
get_time() {
    local -i days hours minutes seconds
    # date returns in 01 02, etc. We need to prepend the formats with
    # 10# so that read doesn't interpret 01, 02, etc as octals.
    [[ "$1" -gt 0 ]] || return
    read days hours minutes seconds <<< \
        "$( date -u -d @${1} "+10#%j 10#%H 10#%M 10#%S" )"
    [[ "$days"    -gt 1 ]]  && echo -n "$(( ${days} - 1 ))d "
    [[ "$hours"   -gt 0 ]]  && echo -n "${hours}h "
    [[ "$minutes" -gt 0 ]]  && echo -n "${minutes}m "
    [[ "$seconds" -gt 0 ]]  && echo -n "${seconds}s "
    echo ""
}

# Place the mouse cursor some lines above
# Usage: go_up <num lines>
go_up() {
    local -i i
    for ((i=1; i<=$1; i++ )); do
        tput cuu1
    done
}

# Usage: has_unsupported_arg <arguments>
has_unsupported_arg() {
    local arg
    for arg in $@; do
        case "$arg" in
            --check-news|--*clean|--config|--deselect)         return 0 ;;
            --list-sets|--metadata|--prune|--regen|--search*)  return 0 ;;
            --sync|--unmerge|--version|--changelog|--columns)  return 0 ;;
            --fetch*|-c|-C|-P|-s|-S|-l|-f|-F)                  return 0 ;;
            --*)    ;; # The other --* commands should be just fine
            -[0-9a-zA-Z]) ;; # The other single letter args are fine
            -*)  has_unsupported_arg $( echo "${arg/-}" | \
                    sed -e 's/\(.\)/ -\1/g' ) &&               return 0 ;;
            *)      ;; # These must be package names
        esac
    done
    return 1    # Good! No unsupported argument has been found!
}

# This is used to print on the left and right with blanks in between
# Usage: infolr <info|infor> "left aligned text" "right aligned text"
infolr() {
    [[ "$3" ]] || {
        $1 "$2$cl"
        return
    }

    # Note that "  * " at the beginning requires 4 characters.
    local left="$( echo "$2" | sed -e 's#\\x1[bB]\[[^m]\+m##g' )"
    local right="$( echo "$3" | sed -e 's#\\x1[bB]\[[^m]\+m##g' )"
    local middle=$(( $cols -4 - ${#left} - ${#right} ))
    [[ $middle -le 0 ]] && middle=1

    $1 "$2${BLANK:1:$middle}$3"
}

# Check whether the last package failed, at the end of emerge process
# Usage: last_pkg_failed <pkg>
last_pkg_failed() {
    local p_tmp p="$1"

    # Check for "$p fail" message
    LC_ALL="C" grep -qi "error.*${p}:.* fail"   $TMPFILE && return 0
    LC_ALL="C" grep -q  "Failed to .*$p"        $TMPFILE && return 0
    LC_ALL="C" grep -qi "ebuild not found.*$p"  $TMPFILE && return 0

    # Check for access violation message in the latest package
    # Sed expression obtained from sed one-liners @ google search:
    #   it prints the line immediately preceding ACCESS... message
    while read p_tmp; do
        p_tmp="${p_tmp#*portage\/}"; p_tmp="${p_tmp%\/work\/*}"
        [[ "$p_tmp" = "$p" ]] && return 0
    done < <( sed -n -e '/ACCESS VIOLATION SUMMARY/{g;1!p;};h' $TMPFILE )
    # Check for fetch restriction fails
    LC_ALL="C" grep -qiE "fetch failed.*$p" $TMPFILE        && return 0

    # $p has been merged successfully
    return 1
}

# Usage: merge_time "cat/pkg-ver"
merge_time() {
    if [[ $_SHOW_MERGE_TIME -eq 1 ]]; then
        get_time "$( my_genlop -t "$1" )"
    else
        echo "${green}Done! $normal"
    fi
}

# Replacement for genlop. This only returns the average time (in seconds)
# needed to emerge a package. If -t is used then it will return the time
# required (in seconds) for the latest merge of the package
# The packagename must be provided without version otherwise it will only
# look for packages with that specific version
# Usage: my_genlop [-t] category/packagename[-ver]
my_genlop() {
    local -i cnt=0 line tmp=0 tmp_sum=0 t=0
    local c p status
    if [[ "$1" = "-t" ]]; then
        t=1
        c="${2%%/*}"
        p="${2##*/}"
    else
        c="${1%%/*}"
        p="${1##*/}"
    fi
    while read line status; do
        # Skip instances of duplicate lines. An example is sys-apps/portage
        if [[ "$status" = "completed" && $tmp -ne 0 ]]; then
            # There are two or more consecutive lines of "completed emerge"
            continue
        elif [[ "$status" = "emerged" && $tmp -eq 0 ]]; then
            # There are two or more consecutive lines of "emerge $pkg"
            continue
        fi
        if [[ "$tmp" -eq 0 ]]; then
            tmp="$line"
        else
            tmp_sum=$(( $tmp_sum + $tmp - $line ))
            ((cnt++))
            tmp=0
            # Use this until I have a better sed expression which can quit
            # immediately after processing the first block of /:::/,/>>>/
            [[ $t -eq 1 ]] && break
        fi
    done < <( \
        if [[ $t -eq 1 ]]; then
            tac $E_LOG | sed -n -e \
                "/:::.* completed emerge .*${c}\/${p}/,/>>>.* emerge .*${c}\/${p}/{s@^\([0-9]\+\):[ ]\+:::.* completed emerge .*${c}/${p}.*@\1 completed@p;s@^\([0-9]\+\):[ ]\+>>>.* emerge .*${c}/${p}.*@\1 emerged@p}"
        else
            sed -n -e \
                "/:::.* completed emerge .*${c}\/${p}-[0-9]\+/,/>>>.* emerge .*${c}\/${p}-[0-9]\+/{s@^\([0-9]\+\):[ ]\+:::.* completed emerge .*${c}/${p}.*@\1 completed@p;s@^\([0-9]\+\):[ ]\+>>>.* emerge .*${c}/${p}.*@\1 emerged@p}" \
                "$tmpT"
        fi )
    [[ "$cnt" -gt 0 ]] && echo "$(( $tmp_sum / $cnt ))"
}

# Function to give the ETA for the emerge of current package or emerge
# list in tmpE
# If the start_time of current pkg is not given then it reads the list
# from tmpE and behaves like "cat $tmpE | genlop -p"
# Usage: my_genlop_eta [<merge start time> <merge eta>]
my_genlop_eta() {
    local -i total_time=0
    if [[ -n "$1" ]]; then
        if [[ $2 -eq 0 ]]; then
            echo ""
        else
            # Use the merge_start and merge_eta variables.
            # Don't call my_genlop all the time
            # total-time = eta - ( curr time - emerge start time )
            total_time="$(( $2 - $( date -u '+%s' ) + $1 ))"
            if [[ "$total_time" -le 0 ]]; then
                echo "any time now "
            else
                get_time "$total_time"
            fi
        fi
    else
        local p ptime
        while read p; do
            # using grep -n and ${ptime%%:*} to get the line number
            ptime="$( echo "$pkg_all" | grep -nF -m1 "$p" )"
            total_time=$(( $total_time + ${merge_eta[${ptime%%:*}]} ))
        done <$tmpE
        if [[ "$total_time" -eq 0 ]]; then
            echo ""
        else
            get_time "$total_time"
        fi
    fi
}

# Use of this function is to make sure tmpfiles do not clash when multiple
# quietemerge instances are running. It does not check if a quietemerge
# instance is already running. So call this function *only* after
# determining that another quietemerge instance is running.
# Usage: new_tmp_file tmpfile
new_tmp_file() {
    # Give a warning if the number of tmp files exceed the number of
    # running quietemerges. Err will output to std. err. so this is fine.
    # Note that the ls command below should not fail since at least two
    # $self process is running.
    [[ "$( ls -1d ${1}* 2>/dev/null | wc -l )" -gt $num_self ]] && \
        Err -w "There are more temporary files $yellow${1}*$normal
    than the number of concurrently running $self processes.
    You can do a 
    ${underline}rm -f $1*$normal
    after all the $self processes have exited ${bold}without error$normal."
    local -i cnt=0
    local t="$1.$cnt"
    
    while [[ -f "$t" ]]; do
        ((cnt++))
        t="$1.$cnt"
    done
    echo "$t"
}


# Usage: parse_cmdline "-some_command"
parse_cmdline() {
    local cmd="$1"
    # Check for -a embedded in the command
    if [[ "$cmd" =~ -[b-zA-Z0-9]*a[b-zA-Z0-9]* ]]; then
        cmd="${cmd/a/}"
        e_opt_ask=1
    fi

    # Check for -p embedded in the command
    if [[ "$cmd" =~ -[a-oq-zA-Z0-9]*p[a-oq-zA-Z0-9]* ]]; then
        cmd="${cmd/p/}"
        e_opt_pretend=1
    fi

    # Check for -q embedded in the command
    if [[ "$cmd" =~ -[a-pr-zA-Z0-9]*q[a-pr-zA-Z0-9]* ]]; then
        cmd="${cmd/q/}"
    fi

    # Check for -r embedded in the command
    if [[ "$cmd" =~ -[a-qs-zA-Z0-9]*r[a-qs-zA-Z0-9]* ]]; then
        cmd="${cmd/r/}"
        e_opt_resume="--resume"
    fi

    # Check for -v embedded in the command
    if [[ "$cmd" =~ -[a-uw-zA-Z0-9]*v[a-uw-zA-Z0-9]* ]]; then
        cmd="${cmd/v/}"
        e_opt_verbose="-v"
    fi

    # If there is any other option left, then assign it
    [[ "$cmd" != "-" ]] && cmdline="$cmdline $cmd"
}

# Emerge has exited. Now check for the remaining packages in tmpE
# Usage: pkg_failed
pkg_failed() {
    local p
    while read p; do
        p="$( echo "${p#*]}" | sed -e 's/^[ ]\+\([^ ]\+\).*$/\1/' )"
        if last_pkg_failed "$p"; then
            ((exit_status++))
            sed -i -e "/${p%%/*}\/${p##*/}/d" $tmpE
            infolr info "Install  $p." "${red}Error!$normal "
        fi
    done <$tmpE
}


# Check temporary files for security related problems
# For now, just delete the file. This should take care of most security
# concerns. 
# Usage: secure_file <filename>
secure_file() {
    # Security measure. Delete the temporary file if it exists.
    [[ -e "$1" ]] && {
        rm -f "$1" || \
        die "Could not delete $yellow$1$normal
    Check if it is a directory, or if it is immutable.\n"
    }
}

# Set the number of columns in the terminal
# Usage: set_cols
set_cols() {
    # Add a small delay and hopefully the window stops resizing.
    sleep 0.1
    cols=$(tput cols)
    printf -vBLANK "%${cols}s" ""
}

# This should modify pkg_installing, pkg_merging, tmpE
# Usage: update_failed <packages>
update_failed() {
    local p
    for p in $@; do
        p="${p%::*}"
        infolr info "Install  $p." "${red}Error!$normal "
        ((exit_status++))
        pkg_installing="${pkg_installing/$p}"
        pkg_merging="${pkg_merging/$p}"
        sed -i -e "/${p%%/*}\/${p##*/}/d" $tmpE
    done
}

# This should modify pkg_installing, pkg_merging and tmpE
# Usage: update_installed <packages>
update_installed() {
    local elog_tail="$( sed -n -e "${num_line_elog},\$p" $E_LOG )"
    local p
    local -i _status=1  # Set to 0 if pkg_installing is modified
    # for each package,
    #   if emerge is complete then remove package from pkg_* and tmpE,
    #   else if it is not in pkg_installing then add it
    for p in $@; do
        p="${p%::*}"
        if echo "$elog_tail" | \
            LC_ALL="C" grep -qs ":::.* completed emerge .*${p}"; then
            infolr info "Install  $p." "$( merge_time "$p" )"
            pkg_installing="${pkg_installing/$p}"
            pkg_merging="${pkg_merging/$p}"
            sed -i -e "/${p%/*}\/${p#*/}/d" "$tmpE"
            _status=0
        elif [[ "${pkg_installing}" != *$p* ]]; then
            pkg_installing="${pkg_installing} $p"
            _status=0
        fi
    done
    return $_status
}

# This should modify pkg_merging
# Usage: update_installing <packages>
update_installing() {
    local p
    for p in $@; do
        p="${p%::*}"
        pkg_merging="${pkg_merging/$p}"
        info "Installing $cyan$p$normal.$cl"
    done
}

# This should modify pkg_merging
# Usage: update_merging <packages>
update_merging() {
    local p
    local -i pnum total_len
    for p in $@; do
        p="${p%::*}"
        # Add package to pkg_merging if not installing or installed
        if [[ "$pkg_merging" != *$p* && "$pkg_installing" != *$p* ]] \
            && grep -qs "$p" $tmpE; then
            pkg_merging="${pkg_merging} $p"
        elif [[ "$pkg_merging" != *$p* ]]; then
            # If the if fails, then it means that $p is a new package and
            # it is either installing, or it has already been installed
            continue
        fi

        # The line number of $p in $pkg_all indexes merge_start
        pnum=$( echo "$pkg_all" | sed -n -e "/${p%%/*}\/${p##*/}/{=;q;}" )
        [[ -z "${merge_start[${pnum}]}" ]] && \
            merge_start[${pnum}]=$(  tac $E_LOG | sed -n -e \
            "/>>>.* emerge .*${p%%/*}\/${p##*/}/{s@^\([0-9]\+\):.*@\1@p;q;}" )

        # Here 15 is the max # of places that will be taken by the display
        # of the ETA. Example: "10d 12h 14m 33s", "any time now"
        total_len=$(( $my_len + ${#p} + 15 ))

        if [[ $total_len -lt $cols ]]; then
            infolr info "Emerging $cyan$p$normal. ETA:" \
                "$green$( my_genlop_eta ${merge_start[${pnum}]} \
                    ${merge_eta[${pnum}]} )$normal"
        elif [[ $(( $total_len - 14 )) -lt $cols ]]; then
            # 14 = $my_len - 5
            # We will print an even more simpler expression!!
            infolr info "$cyan$p$normal:" \
                "$green$( my_genlop_eta ${merge_start[${pnum}]} \
                    ${merge_eta[${pnum}]} )$normal"
        else
            info "Emerging $cyan$p$normal.$cl"
        fi
    done
}

# Usage: update_skipped <packages>
# These packages are never put into pkg_installing or pkg_merging so we
# don't have to remove them from those variables.
update_skipped() {
    local p
    for p in $@; do
        p="${p%::*}"
        # Delete packages from tmpE so that count is correct.
        sed -i -e "/${p%%/*}\/${p##*/}/d" $tmpE
        ((exit_status[1]++))
        infolr info "Skipped  $p (keep-going)." "${pink}Skip!$normal "
    done
}

# Usage: update_term_title
update_term_title() {
    # Don't try to update the terminal title of console!
    [[ "$DISPLAY" ]] || return
    [[ -f $tmpE ]]   || return

    local -i num_jobs="$( echo $pkg_installing $pkg_merging | wc -w )"
    # eta holds the total ETA
    eta="$( my_genlop_eta )"
    # Change the terminal title
    if [[ $num_jobs -eq 1 ]]; then
        local p="$( echo $pkg_installing$pkg_merging | \
            sed -e 's/^[^/]\+\///' -e 's/-[0-9]\+.*//' )"
        echo -ne "\x1b]0;${p} \
, $(($_total_num - $(wc -l <$tmpE) + $num_jobs))/$_total_num, \
Total ETA: ${eta}\007"
    else
        echo -ne "\x1b]0;${num_jobs} \
jobs, $(($_total_num - $(wc -l <$tmpE) + $num_jobs))/$_total_num, \
Total ETA: ${eta}\007"
    fi
}

# End. Fns specific to script }}}


# First, check if any unsupported argument was passed. If passed, then
# execute emerge directly, bypassing $self.
if has_unsupported_arg "$@"; then
    Err -w "Some argument(s) passed to $self are not supported.
    Executing emerge directly."
    exec emerge $@
    exit        # Not needed actually. But keep it for kicks.
fi

# Try to exit gracefully.
trap "exit_prog 1" 1 2 5 15
trap "set_cols" WINCH

# Main program starts here.
# Nice output
# Need to check for NOCOLOR in make.conf first
if grep -qsE "NOCOLOR=([\'\"])?true([\'\"])?" \
    /$(portageq envvar PORTAGE_CONFIGROOT)/etc/{,portage/}make.conf; then
    e_opt_color="--color=n"
fi

# Now parse the command line
until [[ -z "$1" ]]; do
    case "$1" in
        -a|--ask)       e_opt_ask=1;;
        --autounmask-write*)
                if [[ "$2" = "y" || "$2" = "n" ]]; then
                    e_opt_auto="--autounmask-write $2"
                    shift
                else
                    e_opt_auto="$1"
                fi ;;
        --color*)
            if [[ "$1" = *=* ]]; then
                if [[ "$1" = *=n ]]; then
                    e_opt_color="--color=n"
                elif [[ "$1" != *=y ]]; then
                    die "Incorrect option to --color" # Some weird option
                fi
            else
                if [[ "$2" = "n" ]]; then
                    e_opt_color="--color=n"
                elif [[ "$2" != "y" ]]; then
                    die "Incorrect option to --color" # Some weird option
                fi
                shift
            fi ;;
        -p|--pretend)   e_opt_pretend=1;;
        -q|--quiet)     ;;
        -r|--resume)    e_opt_resume="--resume";;
        -v|--verbose)   e_opt_verbose="-v";;
        --*)            cmdline="$cmdline $1";;
        -*)             parse_cmdline "$1";;
        *)              cmdline="$cmdline $1";;
    esac
    shift
done


# one final place where --ask can be hidden: EMERGE_DEFAULT_OPTS!!
# Also check EMERGE_DEFAULT_OPTS for --color=n
fix_edo

[[ "$e_opt_color" = *=n ]] && unset ${COLORS[@]}
set_cols            # Set the columns in the terminal
get_num_self        # Get the number of $self processes running
# Fix the temporary files if multiple $self are running.
[[ $num_self -gt 1 ]] && {
    TMPFILE="$( new_tmp_file "$TMPFILE" )"
    tmpE="$( new_tmp_file "$tmpE" )"
    tmpT="$( new_tmp_file "$tmpT" )"
}

# Secure temporary file tmpE:
secure_file $tmpE

# Pretend emerge. If it fails, exit. Also do some error checking.
centered_output dash " Pretended emerge "
emerge -p $e_opt_color $e_opt_resume $e_opt_verbose $cmdline | tee -i $tmpE
status="${PIPESTATUS[0]}"

# Clean up tmpE of all colors
sed -i -e 's/\[[^m]*m//g' $tmpE
# Keep the list of all packages - used with merge_eta & merge_start
pkg_all="$( sed -r -e '/^\[(ebuild|binary)/!d' $tmpE )"


[[ $status -eq 0 || ( $status -eq 1 && "$e_opt_auto" ) ]] && {
    # Secure temporary file tmpT:
    secure_file $tmpT
    # Dump the emerge.log into tmpT
    tac $E_LOG > $tmpT
    # Get the total number of lines in old emerge.log. It's used later.
    num_line_elog=$( wc -l <$tmpT )

    echo 
    if [[ $_ETA_ALL -eq 1 ]]; then
        LC_ALL="C" grep -qsE "^\[(ebuild|binary)" $tmpE && \
            centered_output blank " Per-package ETA "
    else
        infor "Estimating total update time ..."
    fi
    determine_merge_eta
    centered_output blank " Total ETA: $( get_time ${merge_eta[0]} )"
    if LC_ALL="C" grep -m1 -qsE "^\[binary " $tmpE; then
        infor "Estimating update time without ${pink}binary$normal emerges ..."
        centered_output blank " Total ETA without ${pink}binary$normal: $(\
            get_time $( determine_merge_eta_no_binary ) )"
    fi
    echo
}

# Now check the command line options for further instructions
[[ $e_opt_pretend -eq 1 ]] && exit_prog $status

# Check for root now
[[ $UID -eq 0 ]] || die "root login is required to actually emerge."

# Now set _ETA_ALL to 0 otherwise my_genlop_eta will give tons of output
_ETA_ALL=0

# Error checking
LC_ALL="C" grep -qE '^\[(ebuild|binary)[ ]*I' $tmpE && \
    Err -w "Some packages require user interaction. This script
    will now exit.
    If you want to emerge all packages other than interactive packages give
    the argument ${cyan}--accept-properties=-interactive$normal to emerge." \
    && exit_prog
LC_ALL="C" grep -q '^\[blocks[ ]*B' $tmpE && \
    Err "Hard blocks have been identified. This script will now exit." && \
    exit_prog $status
LC_ALL="C" grep -q '^\[blocks[ ]*b' $tmpE && \
    info "Soft blocks have been identified. The emerge process will resolve
    the blocks automatically.\n"
LC_ALL="C" grep -qE '^\[(ebuild|binary)[^]]+F' $tmpE && \
    Err -w "Packages with fetch restrictions have been identified. The
    emerge process will fail if the packages are not downloaded manually.
    " && sleep 2
# More error checks. Check if emerge exited with 0 return value.
if [[ $status -eq 1 && "$e_opt_auto" ]]; then
    if grep -qs "emerge: there are no ebuilds to satisfy" $tmpE; then
        exit_prog $status
    fi
    info "It seems emerge exited with a non-zero status and you provided the
    --autounmask-write flag either in command line or via EMERGE_DEFAULT_OPTS.
    emerge might modify your config files in ${blue}/etc/portage$normal\n"
elif [[ $status -ne 0 ]]; then
    exit_prog $status
fi
unset status

# --ask was provided in some form in command line.
[[ $e_opt_ask -eq 1 ]] && {
    # Provide a total estimate of the time
    infon "Continue with the emerge process? \
([${green}Y${normal}es]|${red}N${normal}o):"
    read -n 1 tmp; echo
    case "$tmp" in
        ""|[yY]*)  ;;
        *)      exit_prog;;
    esac
}

# Fix the output in tmpE -- keep only packages to be emerged.
echo "$pkg_all" > $tmpE

# Total number of packages which are going to be emerged.
_total_num=$(wc -l <$tmpE)

# Mount the tmpfs if asked for
mount_tmpfs
# Pre-command just before the emerge starts running.
[[ "$_PRE_CMD" ]] && info "Executing pre-command: $_PRE_CMD"
[[ "$_PRE_CMD" && -x "$( which "$_PRE_CMD" 2> /dev/null )" ]] \
    && "$_PRE_CMD" || eval "$_PRE_CMD"
# Export notitles FEATURE so that I can remove my own code
export FEATURES="${FEATURES} notitles"
# Secure temporary files TMPFILE
secure_file $TMPFILE
# Start the actual emerge
emerge -q $e_opt_auto $e_opt_resume --color=n $cmdline >& $TMPFILE &
PID=$!
# Store the emerge start time
merge_eta[0]=$( date -u '+%s' )

echo
if [[ "$e_opt_resume" = "--resume" ]]; then
    centered_output dash " Resuming merge "
else
    centered_output dash " Starting emerge "
fi
echo

# Wait till emerge starts spitting out "emerging cat/pkg"
while ps -p $PID >& /dev/null && \
    ! LC_ALL="C" grep -qsE -m1 "(Emerging|Installing)" $TMPFILE; do
    sleep 1
done


while ps -p $PID >& /dev/null; do
    # Some package is still being emerged. Here count_lines is always one
    # more than what was present. If file was empty then it is 1, if there
    # are 10 lines then it is 11, etc.
    newlines="$( sed -n -e "${count_lines},\$p" $TMPFILE )"

    # Need to move the cursor up by some lines
    go_up $( echo $pkg_merging $pkg_installing | wc -w )

    # update_failed:        modify  pkg_installing  pkg_merging tmpE
    # update_installed:     modify  pkg_installing  pkg_merging tmpE
    # update_installing:    modify                  pkg_merging
    # update_merging:       modify                  pkg_merging
    # update_skipped:       modify                              tmpE
    # update_merging must also check tmpE and pkg_installing to see pkg is
    # still present and is not already installing. This is to consider case
    # when the pkg has 'Emerging' and 'Installing' within $DELAY seconds,-
    # then it might have already installed or it might have already passed
    # the 'Emerging' stage.
    if [[ -z "$newlines" ]]; then
        # no additional lines of output from emerge
        # But we need to update the terminal title if some pkg is installed
        update_installed    ${pkg_installing} && update_term_title
        update_installing   ${pkg_installing}
        update_merging      ${pkg_merging}
    else
        # First output the installed packages. Each pkg is in a new line.
        tmp="$( echo "$newlines" | sed -n -e \
            '/Installing/s/.*) \([^ ]*\)[ ]\?.*$/\1/p' )"
        update_installed    ${pkg_installing} ${tmp}
        # Now look for packages which have failed
        # update_failed should take care of skipped packages too
        tmp="$( echo "$newlines" | sed -n -e \
          '/Failed /{s@.*Failed .*[ ]\+\([^ /]*\)/\([^, ]*\)[, ].*$@\1/\2@p}' )"
        update_failed       ${tmp}
        # Update the packages which were skipped
        tmp="$( echo "$newlines" | sed -n -e \
            '/emerge --keep-going:/{s@.*: \([^ ]*\) dropped.*@\1@p}' )"
        update_skipped      ${tmp}
        # Now the packages which are still freaking installing
        update_installing   ${pkg_installing}
        # Now the packages which are still merging
        tmp="$( echo "$newlines" | sed -n -e \
            '/Emerging/s/.*) \([^ ]*\)[ ]\?.*$/\1/p' )"
        update_merging      ${pkg_merging} ${tmp}
        update_term_title
        count_lines=$(( $( echo "$newlines" | wc -l ) + $count_lines ))
    fi

    sleep $_DELAY
done

# Store the total time taken for emerge to finish
merge_eta[0]=$(( $( date -u '+%s' ) - ${merge_eta[0]} ))
# Either the emerge process is finished or there has been some
# error in the process
newlines="$( sed -n -e "${count_lines},\$p" $TMPFILE )"
go_up $( echo $pkg_merging $pkg_installing | wc -w )
tmp="$( echo "$newlines" | sed -n -e \
    '/Installing/s/.*) \([^ ]*\)[ ]\?.*$/\1/p' )"
update_installed    ${pkg_installing} ${tmp}
tmp="$( echo "$newlines" | sed -n -e \
    '/Failed /{s@.*Failed .*[ ]\+\([^ /]*\)/\([^, ]*\)[, ].*$@\1/\2@p}' )"
update_failed       ${tmp}
tmp="$( echo "$newlines" | sed -n -e \
    '/emerge --keep-going:/{s@.*: \([^ ]*\) dropped.*@\1@p}' )"
update_skipped      ${tmp}
# Now check what is the status of the packages left in $tmpE
# We don't need to bother with pkg_installing and pkg_merging anymore
pkg_failed
LC_ALL="C" grep -qiE "^\!\!\!.*(abort|error)" $TMPFILE && ((exit_status++))

if [[ $_total_num -gt 1 ]]; then
    centered_output blank " Total time taken: $( get_time ${merge_eta[0]} )"
    echo
fi

if [[ $exit_status -ne 0 ]]; then
    # Some package or the other exited with error
    _total_num=$(wc -l <$tmpE)
    Err "Emerge: $red$exit_status failed, $pink${exit_status[1]} skipped, \
$green$_total_num left$normal
    Some package(s) failed. The last few lines of emerge are:
$(tail -n 25 $TMPFILE)

    See $yellow$TMPFILE$normal for the full error message.
    "
    sleep 1
fi

# The emerge process is over. Now check for preserved rebuild notice
tail $TMPFILE | LC_ALL="C" grep -qi "preserved" && \
    info "Preserved libraries found: See $yellow$TMPFILE$normal"

# Update config files.
if [[ "$_TXT_UPDATER" || "$_GUI_UPDATER" ]] && \
    tail $TMPFILE | LC_ALL="C" grep -qEi -m1 \
    "(config.*updat|run etc-update|run dispatch-conf)"; then
    centered_output dash " Updating config files "

    # Handle some error cases
    [[ "$_TXT_UPDATER" && -z "$_GUI_UPDATER" ]] && \
        _GUI_UPDATER="$_TXT_UPDATER"

    if [[ -n "$DISPLAY" ]]; then
            $_GUI_UPDATER
    elif [[ "$_TXT_UPDATER" ]]; then
            $_TXT_UPDATER
    else
        Err "No config file updater was found. Check your settings in
    $yellow$config_dir/$config_file$normal\n"
    fi
elif tail $TMPFILE | LC_ALL="C" grep -qEi -m1 "config.*updat"; then
    info "There are pending configuration updates:
$(tail $TMPFILE)

    See $yellow$TMPFILE$normal for more information.
"
fi

exit_prog $exit_status

# vim: set ai et fdm=marker ff=unix sta sts=4 sw=4 ts=4 tw=75 : 
